package arithmetic.socket.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 写nio的selector模式服务端
 * 一个线程处理多个请求
 * 也有bug 断掉一个客服端就死循环.
 *
 * @author jiangfeng
 * Created on 2020-03-28
 */
public class NioTestServer {

    public static void main(String[] args) throws Exception {
        Selector selector = Selector.open();

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(8899));
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        final List<SocketChannel> channels = new ArrayList<>();

        while (true) {
            int select = selector.select();
            System.out.println("事件数:"+select);
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            selectionKeys.forEach(selectionKey -> {
                if (selectionKey.isAcceptable()) {
                    // 记录所有连接
                    ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
                    try {
                        // 真正建立客户端的连接
                        SocketChannel client = server.accept();
                        System.out.println("客服端连接:" + client.getRemoteAddress());
                        client.configureBlocking(false);
                        client.register(selector, SelectionKey.OP_READ);
                        // 收集记录客户端的连接,实现服务端信息分发
                        channels.add(client);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else if (selectionKey.isReadable()) {
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    // 读取数据
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    try {
                        int read = channel.read(byteBuffer);
                        //读取到数据
                        if (read > 0) {
                            byteBuffer.flip();

                            String s1 = StandardCharsets.UTF_8.decode(byteBuffer).toString();
                            // 打印读到数据
                            String s = channel.getRemoteAddress() + ":" + s1;
                            System.out.println(s);
                            // 分发数据
                            for (SocketChannel socketChannel : channels) {
                                // 重新利用之前的buffer
                                byteBuffer.clear();
                                byteBuffer.put(s.getBytes(StandardCharsets.UTF_8));
                                byteBuffer.flip();
                                socketChannel.write(byteBuffer);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            // 一次循环后要处理完各个事件要清理事件
            selectionKeys.clear();
        }
    }
}
